package hao.itheima.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import hao.itheima.entity.*;
import hao.itheima.mapper.EmpExprMapper;
import hao.itheima.mapper.EmpLogMapper;
import hao.itheima.mapper.EmpMapper;
import hao.itheima.service.EmpService;
import hao.itheima.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class EmpServiceImpl implements EmpService {

    //1、首先面向接口编程, 并进行依赖注入
    @Autowired
    private EmpMapper empMapper;
    @Autowired
    private EmpExprMapper empExprMapper;

    //写service比较规范一点, 也可以直接注入Mapper层, 只不过的加上@Transactional注解
    @Autowired
    private EmpLogMapper empLogMapper;

    /**
     * 分页查询
     *
     * @param empQueryParam 当前页码
     * @return
     */
   /* @Override
    public PageBean page(Integer page, Integer pageSize) {
        //原始方式:
        //1.调用Mapper获取总记录数
        Long total = empMapper.count();
        //2.调用Mapper获取当前页的数据
        Integer start = (page - 1) * pageSize; //计算实际索引
        //的用对象来接
        List<Emp> rows = empMapper.page(start, pageSize);
        //2.封装PageBean
        //3.进行返回


        //分页插件
        //1.设置分页参数
        PageHelper.startPage(page, pageSize);
        //2.调用mapper的列表查询方法
        List<Emp> emplist = empMapper.list();

        //细节2: 分页只会对PageHelper.startPage()下的第一条selelct进行处理
        //如果还想分页生效, 就加上 PageHelper.startPage(page, pageSize);  他跟查询语句是一一对应的关系
        //List<Emp> emplist2 = empMapper.list();

        // 返回的这个对象, 底层已经叫做分页对象了
        //泛型,可写,可不写
        Page<Emp> empPage = (Page<Emp>) emplist; // 强转对象, Page继承了ArrayList
        //3.封装PageBean对象并返回
        //写法很多
        return new PageBean(empPage.getTotal(), empPage.getResult());
        //return new PageBean(empPage.getTotal(), p);
        //下面的两种是,极力不推荐的
        // return new PageBean(empPage.getTotal(), emplist);
        //return new PageBean((long) emplist.size(),emplist);
    }*/
    @Override
    public PageBean page(EmpQueryParam empQueryParam) {

        //1.设置分页参数
        PageHelper.startPage(empQueryParam.getPage(), empQueryParam.getPageSize());
        //2.调用mapper的列表查询方法
        List<Emp> emplist = empMapper.list(empQueryParam);


        // 返回的这个对象, 底层已经叫做分页对象了
        //泛型,可写,可不写
        Page<Emp> empPage = (Page<Emp>) emplist; // 强转对象, Page继承了ArrayList
        //3.封装PageBean对象并返回
        //写法很多
        return new PageBean(empPage.getTotal(), empPage.getResult());

    }

    /**
     * 新增员工
     *
     * @param emp
     */
    @Transactional(rollbackFor = Exception.class) // 开启事务, rollbackFor指定处理所有异常, 默认情况相爱只能出来RuntimeException
    @Override
    public void save(Emp emp) {
        try {
            // 如果有一些字段时没有的, 又需要进行补充, 可以查看页面原型号, 一般都是有说的
            //1、调用Mapper方法, 保存员工的基本信息到emp表
            //1.补充缺失字段
            //实际生产环境中, 是不会写明文的, 一旦数据库进行了泄露,就很危险
            //一般都会加密之后,在进行存储, 加密之后再进行存储, 就算数据库泄露了,信息也不回泄露
            emp.setPassword("123456");
            emp.setCreateTime(LocalDateTime.now());
            emp.setUpdateTime(LocalDateTime.now());
            //2.直接直接调用Mapper
            empMapper.insert(emp);

            //前端没有传id, 就是null, 这里点getId()是拿不到的
            Integer id = emp.getId();
            //为什么要拿id, 因为需要与员工表有关联关系, 等会的设置外键字段(添加员工表的id吗)
            log.info("id={}", id);
            //模拟异常
//        int i = 1 /0;

            //2.调用Mapper方法, 保存员工的基本信息到emp_expr表
            List<EmpExpr> exprList = emp.getExprList();
            //2.1关联员工id(明确工作经历是属于谁的)
            if (!CollectionUtils.isEmpty(exprList)) {
                exprList.forEach(expr -> expr.setEmpId(id));
                //2.2进行批量插入数据
                //调用Mapper, 批量保存工作经历数据
                empExprMapper.insertBatch(exprList);
            }
        } finally {
            //需求: 无论新增员工成功与否, 都需要添加操作日志
            EmpLog empLog = new EmpLog();
            empLog.setOperateTime(LocalDateTime.now());
            empLog.setInfo("新增员工成功" + emp);
            empLogMapper.insert(empLog);
        }


    }

    /**
     * 删除员工信息与员工经历
     *
     * @param ids
     */
    //涉及两张表, 需要进行事务处理
    @Transactional
    @Override
    public void delete(List<Integer> ids) {
        //1、调用Mapper方法,批量删除员工信息
        // Batch 批 一批 的意思
        empMapper.deleteBatch(ids);
        //2、调用Mapper方法, 批量删除员工经历信息
        empExprMapper.deleteBatch(ids);
    }

    @Override
    public Emp getById(Integer id) {
        //方式一:
        //1.调用mapper的查询方法, 获取员工基本信息以及员工经历信息
//        return empMapper.getById(id);

        //方式二:
        //1.查询员工基本信息, 封装到Emp对象中
        Emp emp = empMapper.getById2(id);
        //2.查询员工工作经历信息, 封装到Emp对象中
        emp.setExprList(empExprMapper.getById(id));
        return emp;
    }


    @Override
    //多张表 开启事务
    @Transactional
    public void update(Emp emp) {
        //1.修改员工的基本信息 -- emp表
        //也会传递时间,但是时间是旧的
        //1.1补充基本字段
        emp.setUpdateTime(LocalDateTime.now());
        //1.2调用mapper进行更新
        empMapper.update(emp);
        //2.修改员工工作经历信息 -- emp_expr表
        //2.1根据emp_id进行删除所有的工作经历
        Integer id = emp.getId();
        //2.1.5调用Mapper方法进行数据删除
        empExprMapper.delete(id);
        //2.1.4获取前端传递过来的所有经历
        List<EmpExpr> exprList = emp.getExprList();
        //2.1.1 前端传递过来的工作经历,第二条是没有emp_id  --->因此要获取对应的id,进行设置
        //2.1.3做极端判断, 不让后面进行新增数据的时候,就会到时sql语法错误
        if (!CollectionUtils.isEmpty(exprList)) {
            //2.1.2 遍历所有的数据,设置对应的id
            exprList.forEach(expr -> {
                expr.setEmpId(id);
            });
            //2.1.6调用Mapper方法进行批量保存
            empExprMapper.insertBatch(exprList);
        }
    }

    /**
     * 查询所有员工
     *
     * @return
     */
    @Override
    public List<Emp> list() {
        List<Emp> empLists = empMapper.list(null);
        return empLists;
    }

    /**
     * 员工登录
     *
     * @param emp
     * @return
     */
    //选中方法, 按下 ctrl + B 可以快速回到上一层调用处
    @Override
    public EmpLoginInfo login(Emp emp) {
        //1.调用Mapper查询员工
        Emp empDB = empMapper.selectUsernameAndPassword(emp);
        //2.判断使用户密码是否正确(其实就是判断查询出来的数据是否为空)
        if (empDB != null) {
            //3.如果查询数据, 就就构造EmpLoginInfo对象并返回
            Map<String, Object> dataMap = new HashMap<>();
            //这时候, 数据就得写, 查询到的id, 后面可能是会使用的
            dataMap.put("id", empDB.getId());
            dataMap.put("username", empDB.getUsername());
            String jwt = JwtUtils.generateJwt(dataMap);
            //输出
            log.info(jwt);
            //这个对象,是属于返回的结果, 可以在controller与service层进行构造
            return new EmpLoginInfo(empDB.getId(), empDB.getUsername(), empDB.getName(), jwt);
        }
        //没有查询到数据,就直接返回null
        return null;
    }
}
